﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Calculate
{
    class functions
    {
        string[] OPERATION_PRIORITY = ["^", "*/", "+-"];
        char[] operations = ['^', '*', '/', '+', '-'];


        int find(string[] where, string value)
        {
            for (int i = 0; i < where.Length; i++)
                if (where[i] == value)
                    return i;
            return -1;
        }
        string[] findExpressionInBKT(string[] formatExpression)
        {
            List<string> expression = new List<string>();
            int bktCount = 0;
            bool isContainsBKT = false;

            for (int i = 0; i < formatExpression.Length; i++)
            {
                if (isContainsBKT)
                    expression.Add(formatExpression[i]);

                if (formatExpression[i] == "(")
                {
                    isContainsBKT = true;
                    bktCount++;
                }
                else if (formatExpression[i] == ")")
                {
                    bktCount--;
                    if (bktCount == 0)
                    {
                        return expression.ToArray()[0..(expression.Count-1)];
                    }
                }
            }
            return new string[0];
        }

        public string[] convertToArray(string expression)
        {
            List<string> res = new List<string>();
            string value = "";

            foreach (var ltr in expression)
            {
                if ((int)ltr >= 48 && (int)ltr <= 57 || ltr == ',')
                    value += ltr;
                else if (operations.Contains(ltr) || ltr == '(' || ltr == ')')
                {
                    if (value != "")
                        res.Add(value);
                    res.Add(ltr.ToString());
                    value = "";
                }
            }
            if (value != "")
                res.Add(value);
            return res.ToArray();
        }

        double makeOperation(string leftTerm, string rightTerm, string oper)
        {
            switch (oper)
            {
                case "^":
                    return Math.Pow(Convert.ToDouble(leftTerm), Convert.ToDouble(rightTerm));
                case "*":
                    return Convert.ToDouble(leftTerm) * Convert.ToDouble(rightTerm);
                case "/":
                    return Convert.ToDouble(leftTerm) / Convert.ToDouble(rightTerm);
                case "+":
                    return Convert.ToDouble(leftTerm) + Convert.ToDouble(rightTerm);
                case "-":
                    return Convert.ToDouble(leftTerm) - Convert.ToDouble(rightTerm);
                default:
                    throw new Exception("Unknown operation");
            }
        }

        int ContainsInArray<T>(T value, T[] array)
        {
            for (int i = 0; i < array.Length; i++)
                if (array[i].Equals(value))
                    return i;
            return -1;
        }

        int getHighLevelOperationIndex(string[] formatExpression)
        {
            int operIndex = 0;
            int? minIndex;
            foreach (var oneLVLoper in OPERATION_PRIORITY)
            {
                minIndex = null;
                foreach (var oper in oneLVLoper)
                {
                    operIndex = ContainsInArray(oper.ToString(), formatExpression);
                    if (operIndex != -1 && (operIndex < minIndex || minIndex == null))
                        minIndex = operIndex;
                }
                if (minIndex != null)
                    return minIndex.Value;

            }
            return -1;
        }
        public double CalculateExpression(string[] formatExpression)
        {
            List<string> newExpression = new List<string>();
            var highOperIndex = getHighLevelOperationIndex(formatExpression);
            if (highOperIndex != -1)
            {
                double resOperation = makeOperation(formatExpression[highOperIndex - 1], formatExpression[highOperIndex + 1], formatExpression[highOperIndex]);
                foreach (var item in formatExpression[0..(highOperIndex-1)])
                    newExpression.Add(item);
                newExpression.Add(resOperation.ToString());
                if (highOperIndex + 2 < formatExpression.Length)
                    foreach (var item in formatExpression[(highOperIndex + 2)..formatExpression.Length])
                        newExpression.Add(item);
                return CalculateExpression(newExpression.ToArray());
            }
            return Convert.ToDouble(formatExpression[0]);
        }

        public double CalculateExpressionWithBKT(string[] formatExpression)
        {
            var expressionInBKT = findExpressionInBKT(formatExpression);
            int beginIndex = find(formatExpression, "(");

            if (expressionInBKT.Length == 0)
                return CalculateExpression(formatExpression);
            else
            {
                List<string> newExpression = new List<string>();
                for (int i = 0; i < beginIndex; i++)
                    newExpression.Add(formatExpression[i]);
                newExpression.Add(CalculateExpressionWithBKT(expressionInBKT).ToString());
                for (int i = (beginIndex + expressionInBKT.Length + 2); i < formatExpression.Length; i++)
                    newExpression.Add(formatExpression[i]);
                return CalculateExpressionWithBKT(newExpression.ToArray());
            }
        }
    }
}
